﻿using System;
using System.Web.Helpers;
using System.Web.Helpers.Fakes;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Ploeh.AutoFixture;
using User.EF.Contracts;
using User.EF.Model;
using User.Global.Constvalues;
using User.Global.Tests.Helper.MockHelper;
using User.Global.Tests.Helper.MoqHelper;
using User.Web.Common.Membership;

namespace User.Web.UI.Tests.Unit.CommonTests
{
    /// <summary>
    /// Alle Passenden Tests für unsere Userverwaltung
    /// </summary>
    [TestClass]
    public class DiMembershipTest
    {
        /// <summary>
        /// Die aktuellen Membership Queries.
        /// </summary>
        private Mock<IWebQuerySummary> MockWebQuerySummary { get; set; }

        /// <summary>
        /// Das aktuelle Di Membership Objekt bei dem alle Funktionen getestet werden.
        /// </summary>
        private DiMembership DiMembership { get; set; }

        #region Initialize
        [TestInitialize]
        public void Initialize()
        {
            MockWebQuerySummary = new Mock<IWebQuerySummary>();

            DiMembership = new DiMembership(MockWebQuerySummary.Object);
            DiMembership.Message = new MockAddMessage();
        }
        #endregion

        #region Tests Login
        /// <summary>
        /// Prüfen das auch eine NullReferenceException geworfen wird, wenn kein
        /// Membership Repository übergeben wird.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(NullReferenceException), "MemberShipQueries are Null")]
        public void DiMembershipCreation_ExceptionIsNull()
        {
            DiMembership membership = new DiMembership(null);
        }

        /// <summary>
        /// Prüft, auf einen erfolgreichen Login
        /// </summary>
        [TestMethod]
        public void Login_Success()
        {
            //http://squadwuschel.wordpress.com/2013/03/21/vs-2012-fake-dlls-fr-tests-stubs-und-shims/
            //Verwenden von Shims von MS Framework zum Faken von Funktionsaufrufen.
            using (ShimsContext.Create())
            {
                Member member = new Member() { Password = Crypto.HashPassword("testpassword"), Username = "squadwuschel@gmx.de" };
                //Damit der Login erfolgreich ist, muss der VerifyPW Hash True zurückgeben.
                ShimCrypto.VerifyHashedPasswordStringString = (p, c) => true;
                //Da wir hier nicht direkt auf "ShimFormsAuthentication" zugreifen können, müssen wir diese Funktion ebenfalls Faken.
                System.Web.Security.Fakes.ShimFormsAuthentication.SetAuthCookieStringBoolean = (c, p) => { };
                //Erstellen eines Members
                MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns(member);
                //Prüfen ob der Login erfolgreich ist.
                Assert.IsTrue(DiMembership.Login("squadwuschel@gmx.de", "testpassword", false));
                MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Once);
            }
        }

        /// <summary>
        /// Prüfen ob der Login abbricht, wenn zu viele Loginversuche stattgefunden haben.
        /// </summary>
        [TestMethod]
        public void Login_Failed_MaxLoginTries()
        {
            //Erstellen eines Members, da die PW Längenprüfung zu beginn der Funktion stattfindet,
            //sind keine Shims notwendig.
            Member member = new Member() { LoginTries = ConstLengthValues.MaxLoginTries + 1 };
            MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns(member);

            Assert.IsFalse(DiMembership.Login(It.IsAny<string>(), It.IsAny<string>(), false));
            //z.B. prüfen, ob die jeweilige funktion auch nur einmal aufgerufen wurde, die wir gemockt haben.
            MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Once);
        }

        /// <summary>
        /// Login Failed weil die PW Verifizierung fehlgeschlagen ist.
        /// </summary>
        [TestMethod]
        public void Login_Failed_WrongPassword()
        {
            using (ShimsContext.Create())
            {
                //Der PW Check schlägt fehl.
                ShimCrypto.VerifyHashedPasswordStringString = (p, c) => false;
                MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns(new Member());

                Assert.IsFalse(DiMembership.Login(It.IsAny<string>(), It.IsAny<string>(), false));
                MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Once);
            }
        }

        /// <summary>
        /// Prüfen ob der User auch existiert 
        /// </summary>
        [TestMethod]
        public void Login_Failed_UserNotFound()
        {
            MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns((Member)null);
            //Da der user nicht gefunden wird, False zurückgeben
            Assert.IsFalse(DiMembership.Login(It.IsAny<string>(), It.IsAny<string>(), false));
            //Prüft alle Abfragen die gemockt wurden auch verwendet wurden, wenn eine abfrage nicht aufgerufen wurde,
            //dann wird hier eine Exception geworfen.
            MockWebQuerySummary.VerifyAll();
        }
        #endregion

        #region Tests CreateUser
        /// <summary>
        /// Erfolgreiches Erstellen eines Users
        /// </summary>
        [TestMethod]
        public void CreateUser_Success()
        {
            using (ShimsContext.Create())
            {
                Member member = new Member() {Password = "testpassword", Username = "squadwuschel"};
                //Hinzufügen eines Users zur DB
                MockWebQuerySummary.Setup(p => p.RightQueries.AddDefaultVocabelRights(It.IsAny<long>()));
                MockWebQuerySummary.Setup(p => p.MembershipQueries.AddMember(It.IsAny<Member>())).Returns(true);
                MockWebQuerySummary.Setup(p => p.Save()).Returns(true);
                //Diese Funktion wird beim Erstellen eines Users mehrmals aufgerufen und hier sind jeweils
                //andere Rückgabewerte notwendig, daher wird beim Ersten Aufrufen null und beim Zweiten ein Member Objekt zurückgegeben
                MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).ReturnsInOrder(null,member );

                //Damit der Login erfolgreich ist, muss der VerifyPW Hash True zurückgeben.
                ShimCrypto.VerifyHashedPasswordStringString = (p, c) => true;
                //Auch das Setzen des AuthenticationCookies darf nicht fehlschlagen
                System.Web.Security.Fakes.ShimFormsAuthentication.SetAuthCookieStringBoolean = (c, p) => { };

                //Assert
                Assert.IsTrue(DiMembership.CreateUser("squadwuschel", "testpassword", member));
                MockWebQuerySummary.Verify(p => p.MembershipQueries.AddMember(It.IsAny<Member>()), Times.Once);
                MockWebQuerySummary.Verify(p => p.Save(), Times.Exactly(2));
                MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Exactly(2));
            }
        }

        /// <summary>
        /// Prüfen ob eine Exception geworfen wird, wenn der Username Null ist.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(NullReferenceException), "Member, password or username is null")]
        public void CreateUser_Exception_UserNameIsNull()
        {
            DiMembership.CreateUser(null, "testpassword", new Member());
        }

        /// <summary>
        /// Prüfen ob eine Exception geworfen wird, wenn das Passwort Null ist.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(NullReferenceException), "Member, password or username is null")]
        public void CreateUser_Exception_PasswordIsNull()
        {
            DiMembership.CreateUser("squadwuschel", null, new Member());
        }

        /// <summary>
        /// Prüfen ob eine Exception geworfen wird, wenn das Member Objekt Null ist.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(NullReferenceException), "Member, password or username is null")]
        public void CreateUser_Exception_MemberIsNull()
        {
            DiMembership.CreateUser("squadwuschel", "testpassword", null);
        }

        /// <summary>
        /// Anlegen des Users fehlgeschlagen, da der User bereits existiert.
        /// </summary>
        [TestMethod]
        public void CreateUser_Failed_UserExists()
        {
            //Bereits einen bestehenden User zurückgeben
            MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns(new Member());

            //Assert.
            Assert.IsFalse(DiMembership.CreateUser("squadwuschel", "testpassword", new Member()));
            MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Once);
        }

        /// <summary>
        /// Anlegen des Users fehlgeschlagen, da das Hinzufügen des Users fehlgeschlagen ist.
        /// </summary>
        [TestMethod]
        public void CreateUser_Failed_AddMember()
        {
            MockWebQuerySummary.Setup(p => p.MembershipQueries.AddMember(It.IsAny<Member>())).Returns(false);
            MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns((Member)null);

            //Assert
            Assert.IsFalse(DiMembership.CreateUser("squadwuschel", "testpassword", new Member()));
            MockWebQuerySummary.Verify(p => p.MembershipQueries.AddMember(It.IsAny<Member>()), Times.Once);
            MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Once);
        }

        /// <summary>
        /// Anlegen des Users fehlgeschlagen, da das Speichern des Users ist fehlgeschlagen.
        /// </summary>
        [TestMethod]
        public void CreateUser_Failed_SaveMember()
        {
            MockWebQuerySummary.Setup(p => p.MembershipQueries.AddMember(It.IsAny<Member>())).Returns(true);
            MockWebQuerySummary.Setup(p => p.Save()).Returns(false);
            MockWebQuerySummary.Setup(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>())).Returns((Member)null);

            //Assert
            Assert.IsFalse(DiMembership.CreateUser("squadwuschel", "testpassword", new Member()));
            MockWebQuerySummary.Verify(p => p.MembershipQueries.AddMember(It.IsAny<Member>()), Times.Once);
            MockWebQuerySummary.Verify(p => p.Save(), Times.Once);
            MockWebQuerySummary.Verify(p => p.MembershipQueries.GetMemberByUsername(It.IsAny<string>()), Times.Once);
        }
        #endregion
    }
}
